package com.ethink.framework.captcha;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.util.IdUtil;
import com.ethink.framework.captcha.properties.CaptchaProperties;
import com.ethink.framework.captcha.model.CaptchaInfo;
import com.ethink.framework.common.exception.BusinessException;
import com.ethink.framework.common.constant.CommonRespCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @Author name(Tel : mobile)
 * @Date 2019-10-09 20:29
 **/
@Slf4j
@Component
@EnableConfigurationProperties({CaptchaProperties.class})
@RefreshScope
public class CaptchaManager {

    private static final String PNG = "png";

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private CaptchaProperties captchaProperties;

    private CircleCaptcha circleCaptcha;

    private RandomGenerator randomGenerator;

    @PostConstruct
    public void init() {
        circleCaptcha = CaptchaUtil.createCircleCaptcha(captchaProperties.getImageWidth(), captchaProperties.getImageHeight(),
                captchaProperties.getCodeCount(), captchaProperties.getImageInterfereCount());
        randomGenerator = new RandomGenerator(captchaProperties.getCodeBaseStr(), captchaProperties.getCodeCount());
    }

    /**
     * 生成验证码
     *
     * @return 验证码实体
     */
    public CaptchaInfo generateSimple() {
        return build();
    }

    /**
     * 验证验证码
     *
     * @param code
     * @param inputCode
     * @return
     */
    public boolean validateSimple(String code, String inputCode) {
        return randomGenerator.verify(code, inputCode);
    }

    /**
     * 生成验证码
     *
     * @return 验证码实体
     */
    public CaptchaInfo generate(String redisKeyPrefix) {
        CaptchaInfo captchaInfo = build();
        redisTemplate.opsForValue().set(redisKeyPrefix + captchaInfo.getImageId(),
                "0:" + captchaInfo.getCaptchaCode(), captchaProperties.getExpire(), TimeUnit.SECONDS);
        return captchaInfo;
    }

    /**
     * 验证验证码
     *
     * @param imageId   验证码图片ID
     * @param inputCode 输入验证码
     * @throws BusinessException
     */
    public void validate(String redisKeyPrefix, String imageId, String inputCode) {
        if (!captchaProperties.isEnabled()) {
            return;
        }
        String captchaCode = redisTemplate.opsForValue().get(redisKeyPrefix + imageId);
        if (StringUtils.isBlank(captchaCode)) {
            throw new BusinessException(CommonRespCode.CAPTCHA_CODE_INVALID);
        }
        String[] parts = captchaCode.split(":");
        int times = Integer.parseInt(parts[0]);


        if (randomGenerator.verify(parts[1], inputCode)) {
            redisTemplate.delete(redisKeyPrefix + imageId);
            return;
        } else {
            //验证码校验次数 +1
            if (times + 1 >= captchaProperties.getMaxValidateTimes()) {
                redisTemplate.delete(redisKeyPrefix + imageId);
            } else {
                redisTemplate.opsForValue().setIfPresent(redisKeyPrefix + imageId,
                        captchaCode.replaceFirst(times + ":", times + 1 + ":"));
            }
        }
        throw new BusinessException(CommonRespCode.CAPTCHA_VERIFY_CODE_ERROR);
    }

    /**
     * 生成验证码
     *
     * @return 验证码实体
     */
    private CaptchaInfo build() {
        String captchaCode = randomGenerator.generate();
        BufferedImage bufferedImage = (BufferedImage) circleCaptcha.createImage(captchaCode);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            ImageIO.write(bufferedImage, PNG, outputStream);
            CaptchaInfo captchaInfo = new CaptchaInfo();
            captchaInfo.setEncodeString(Base64Utils.encodeToString(outputStream.toByteArray()));
            captchaInfo.setImageId(IdUtil.simpleUUID());
            captchaInfo.setCaptchaCode(captchaCode);
            return captchaInfo;
        } catch (Exception e) {
            throw new BusinessException(CommonRespCode.SYSTEM_ERR, e);
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                throw new BusinessException(CommonRespCode.SYSTEM_ERR, e);
            }
        }
    }

}
